-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZSA integration (step 4): Refactor Orchard structures to generics and add Orchard ZSA support for Transaction V6 #17
base: zsa-integration-txv6
Are you sure you want to change the base?
Conversation
…(without unit tests fixing for now). - Refactored `ShieldedData` and `Action` structures to be generics parameterized by Orchard flavor (`OrchardVanilla` or `OrchardZSA`), enabling support for both Orchard protocols in Tx V6. - Introduced a `burn` field in `ShieldedData` to support ZSA, with unit type for Tx V5 and a vector of burn items for Tx V6. - Modified `Transaction` enum methods (orchard_...) to handle generics properly, ensuring compatibility with both Orchard flavors. - Implemented serialization and deserialization for Tx V6 while avoiding code redundancy with Tx V5 wherever possible.
…m/QED-it/zebra into switch-to-zsa-crates-nu6-txv6-gen
… with the upstream halo2/librustcash/orchard/sapling versions
…-zsa-crates-nu6-txv6-gen
…witch-to-zsa-crates-nu6-txv6-gen
…t_desc to convert slice to vec, as slices require implementation of the serialization from scratch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comments.
In addition,
- for Serialize / De- Serialize let's use the functions from
librustzcash
instead of duplicating them. Especially forIssuance
(a completely new component in Zebra.) - Need to properly handle feature flags. Currently, it is an unclear why 2 different flags are used.
RUSTFLAGS: '--cfg zcash_unstable="nu6"' | ||
RUSTDOCFLAGS: '--cfg zcash_unstable="nu6"' | ||
#RUSTFLAGS: '--cfg zcash_unstable="nu6"' | ||
#RUSTDOCFLAGS: '--cfg zcash_unstable="nu6"' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why comment out?
|
||
pub(crate) use shielded_data::ActionCommon; | ||
|
||
#[cfg(feature = "tx-v6")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not #[cfg(zcash_unstable = "nu6")]
we don't need them both, need to decide on one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like tx-v6
better because I think we just need the feature to ensure that v6 transactions can't be (de)serialized by versions of Zebra that aren't ready to deploy NU7, but either seems fine.
/// the transactions `V5` and `V6`. | ||
pub trait OrchardFlavorExt: Clone + Debug { | ||
/// A type representing an encrypted note for this protocol version. | ||
/// A type representing an encrypted note for this protocol version. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicated line
@@ -0,0 +1,84 @@ | |||
//! This module defines traits and structures for supporting the Orchard Shielded Protocol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this file? why not use orchard:: orchard_flavor
directly and impl
the missing parts?
@@ -93,7 +94,8 @@ impl ZcashDeserialize for Action { | |||
// https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to | |||
// 580 bytes in https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus | |||
// See [`note::EncryptedNote::zcash_deserialize`]. | |||
enc_ciphertext: note::EncryptedNote::zcash_deserialize(&mut reader)?, | |||
// FIXME: don't mention about 580 here as this should work for OrchardZSA too? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, can remove comment.
@@ -41,7 +40,7 @@ fn generate_test_vectors() { | |||
let anchor_bytes = [0; 32]; | |||
let note_value = 10; | |||
|
|||
let shielded_data: Vec<zebra_chain::orchard::ShieldedData> = (1..=4) | |||
let shielded_data: Vec<zebra_chain::orchard::ShieldedData<OrchardVanilla>> = (1..=4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let shielded_data: Vec<ShieldedData<OrchardVanilla>> = (1..=4)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjective: I think orchard::ShieldedData
would be slightly more readable, though every option seems fine.
|
||
let bundle = bundle | ||
.create_proof(&proving_key, rng) | ||
.unwrap() | ||
.apply_signatures(rng, [0; 32], &[]) | ||
.unwrap(); | ||
|
||
zebra_chain::orchard::ShieldedData { | ||
zebra_chain::orchard::ShieldedData::<OrchardVanilla> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ShieldedData::<OrchardVanilla> {
@@ -91,7 +91,7 @@ fn generate_test_vectors() { | |||
// FIXME: support OrchardZSA too, 580 works for OrchardVanilla only! | |||
// FIXME: consider more "type safe" way to do the following conversion | |||
// (now it goes through &[u8]) | |||
enc_ciphertext: <[u8; 580]>::try_from( | |||
enc_ciphertext: <[u8; OrchardVanilla::ENCRYPTED_NOTE_SIZE]>::try_from( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type is already known
enc_ciphertext: a.encrypted_note().enc_ciphertext.as_ref().try_into().unwrap().into(),
let maybe_shielded_data: Option<zebra_chain::orchard::ShieldedData<OrchardVanilla>> = | ||
bytes | ||
.zcash_deserialize_into() | ||
.expect("a valid orchard::ShieldedData instance"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let shielded_data = zebra_test::vectors::ORCHARD_SHIELDED_DATA
.clone()
.iter()
.map(|bytes| {
bytes
.zcash_deserialize_into::<ShieldedData<OrchardVanilla>>()
.expect("a valid orchard::ShieldedData instance")
})
.collect();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should deserialize to None
if there are no actions for OrchardVanilla (see comment on ZcashDeserialize
impl about burns).
let maybe_shielded_data: Option<zebra_chain::orchard::ShieldedData<OrchardVanilla>> = | ||
bytes | ||
.zcash_deserialize_into() | ||
.expect("a valid orchard::ShieldedData instance"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial initial review, looking great so far!
|
||
// These impls all only exist because of array length restrictions. | ||
// TODO: use const generics https://github.com/ZcashFoundation/zebra/issues/2042 | ||
|
||
impl Copy for EncryptedNote {} | ||
impl<const N: usize> Copy for EncryptedNote<N> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: The Copy
, Clone
, and Debug
traits here can be derived now.
/// A structure representing a tag for Orchard protocol variant used for the transaction version `V6` | ||
/// (which ZSA features support). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick/Optional:
/// A structure representing a tag for Orchard protocol variant used for the transaction version `V6` | |
/// (which ZSA features support). | |
/// A structure representing a tag for Orchard protocol variant used for the transaction version 6 | |
/// (with support for ZSAs). |
|
||
impl ZcashSerialize for NoBurn { | ||
fn zcash_serialize<W: io::Write>(&self, mut _writer: W) -> Result<(), io::Error> { | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, anytime there's a Vec being serialized, there's a CompactSize
first, so it does need to insert a 0. Serializing an empty Vec
should work too. Or zcash_serialize_empty_list()
.
Update:
There should be a 0 there for version 6 transactions and nothing for version 5 transactions, the code is correct as-is.
@@ -20,9 +20,17 @@ use crate::{ | |||
}, | |||
}; | |||
|
|||
use super::OrchardFlavorExt; | |||
|
|||
#[cfg(not(feature = "tx-v6"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need OrchardVanilla
for v5 transactions
#[cfg(not(feature = "tx-v6"))] |
// The following expression doesn't work for generics, so a workaround with _ACTION_MAX_ALLOCATION_OK in | ||
// AuthorizedAction impl is used instead: | ||
// static_assertions::const_assert!(AuthorizedAction::<V>::ACTION_MAX_ALLOCATION < (1 << 16)); | ||
AuthorizedAction::<V>::ACTION_MAX_ALLOCATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size_of()
might work here.
let result = Action::<OrchardVanilla>::max_allocation(); | ||
|
||
// TODO: FIXME: Check this: V6 is used as it provides the max size of the action. | ||
// So it's used even for V5 - is this correct? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to use the smaller one. It's okay if the max allocation is a little too large.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add copies of the updated tests for OrchardZSA
as well?
|
||
/// Represents an Orchard ZSA burn item. | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub struct BurnItem(AssetBase, Amount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valueBurn
is specified as a uint64
, but Amount
is an i64.
pub struct BurnItem(AssetBase, Amount); | |
pub struct BurnItem(AssetBase, u64); |
#[cfg(not(feature = "tx-v6"))] | ||
let result = Action::<OrchardVanilla>::max_allocation(); | ||
|
||
// TODO: FIXME: Check this: V6 is used as it provides the max size of the action. | ||
// So it's used even for V5 - is this correct? | ||
#[cfg(feature = "tx-v6")] | ||
let result = Action::<OrchardZSA>::max_allocation(); | ||
|
||
result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This suggestion relies on moving OrchardVanilla
out from behind the feature flag.
#[cfg(not(feature = "tx-v6"))] | |
let result = Action::<OrchardVanilla>::max_allocation(); | |
// TODO: FIXME: Check this: V6 is used as it provides the max size of the action. | |
// So it's used even for V5 - is this correct? | |
#[cfg(feature = "tx-v6")] | |
let result = Action::<OrchardZSA>::max_allocation(); | |
result | |
Action::<OrchardVanilla>::max_allocation() |
impl TrustedPreallocate for BurnItem { | ||
fn max_allocation() -> u64 { | ||
// FIXME: is this a correct calculation way? | ||
// The longest Vec<BurnItem> we receive from an honest peer must fit inside a valid block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is correct as-is, it should be (MAX_BLOCK_BYTES - 1) / BURN_ITEM_SIZE
and it should be implemented for BurnItem
, not Vec<BurnItem>
.
} | ||
|
||
#[cfg(any(test, feature = "proptest-impl"))] | ||
impl serde::Serialize for BurnItem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that it's qualified as serde::Serialize
since ZcashSerialize
is also used in this file, but we probably don't need this implementation at all. If we do need it, it could probably be conditionally derived with #[cfg_attr(condition, attribute)]
.
@@ -41,7 +40,7 @@ fn generate_test_vectors() { | |||
let anchor_bytes = [0; 32]; | |||
let note_value = 10; | |||
|
|||
let shielded_data: Vec<zebra_chain::orchard::ShieldedData> = (1..=4) | |||
let shielded_data: Vec<zebra_chain::orchard::ShieldedData<OrchardVanilla>> = (1..=4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjective: I think orchard::ShieldedData
would be slightly more readable, though every option seems fine.
@@ -91,7 +91,7 @@ fn generate_test_vectors() { | |||
// FIXME: support OrchardZSA too, 580 works for OrchardVanilla only! | |||
// FIXME: consider more "type safe" way to do the following conversion | |||
// (now it goes through &[u8]) | |||
enc_ciphertext: <[u8; 580]>::try_from( | |||
enc_ciphertext: <[u8; OrchardVanilla::ENCRYPTED_NOTE_SIZE]>::try_from( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enc_ciphertext: a.encrypted_note().enc_ciphertext.0.into(),
let maybe_shielded_data: Option<zebra_chain::orchard::ShieldedData<OrchardVanilla>> = | ||
bytes | ||
.zcash_deserialize_into() | ||
.expect("a valid orchard::ShieldedData instance"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should deserialize to None
if there are no actions for OrchardVanilla (see comment on ZcashDeserialize
impl about burns).
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { | ||
// Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. | ||
let actions: Vec<orchard::Action> = (&mut reader).zcash_deserialize_into()?; | ||
let actions: Vec<orchard::Action<V>> = (&mut reader).zcash_deserialize_into()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method returns None
if actions.is_empty()
, but it likely needs to check for asset burns as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add copies of the updated tests for OrchardZSA as well?
This PR refactors the Orchard-related structures to be generics and adds support for both Orchard Vanilla and Orchard ZSA in Zebra's Transaction V6. This change allows the codebase to handle both versions of the Orchard protocol and implements necessary serialization/deserialization logic.
Key changes
ShieldedData
andAction
structures to be generic, parameterized by Orchard flavor (OrchardVanilla
orOrchardZSA
), enabling support for both protocols in Tx V6.burn
Field inShieldedData
:burn
field toShieldedData
to support ZSA: a unit type for Tx V5 and a vector of burn items for Tx V6.Transaction
Enum Methods:Transaction
enum to handle the new generic structures properly and support both Orchard flavors.